package com.cqw.aspect;

import com.alibaba.fastjson.JSON;
import com.cqw.annotation.RepeatSubmit;
import com.cqw.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: caoqingwen
 * @CreateTime: 2021-06-02 22:33
 * @Description: 重复提交切面
 */
@Aspect
@Slf4j
public class RepeatSubmitAspect {


    private static final String PREFIX_KEY = "common:mideng:";


    @Autowired
    private RedisTemplate redisTemplate;


    /**
     * 声明切面
     */
    private static final String POINT_CUT = "@annotation(com.cqw.annotation.RepeatSubmit)";


    /**
     * 方法执行之前判断
     *
     * @param joinPoint
     * @throws Throwable
     */
    @Around(POINT_CUT)
    public Object aroundAop(ProceedingJoinPoint joinPoint) throws Throwable {
        //判断参数是否一样
        CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

        RepeatSubmit repeatSubmit = methodSignature.getMethod().getAnnotation(RepeatSubmit.class);
        if (repeatSubmit != null) {

            ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attribute.getRequest();

            // 请求地址（作为存放cache的key值）
            String requestURL = request.getRequestURI();

            requestURL = requestURL.replaceAll("/",":");

            //参数
            Map<String, Object> params = getParams(codeSignature, joinPoint);
            String nowParams = JSON.toJSONString(params);

            String key = PREFIX_KEY + requestURL;

            Object o = redisTemplate.opsForValue().get(key);
            if (o != null) {
                //比较参数
                String oldParams = (String) o;
                if (oldParams.equals(nowParams)) {
                    throw new CommonException("请勿重复提交");
                }
            }
            log.info("创建幂等：{}:{}", key, nowParams);
            redisTemplate.opsForValue().set(key, nowParams);
        }
        //继续执行
        Object proceed = joinPoint.proceed();
        return proceed;
    }


    /**
     * 执行完之后，将redis删除
     */
    @AfterReturning(POINT_CUT)
    public void afterReturn(JoinPoint joinPoint) {
        ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attribute.getRequest();

        // 请求地址（作为存放cache的key值）
        String requestURL = request.getRequestURI();

        requestURL = requestURL.replaceAll("/",":");

        String key = PREFIX_KEY + requestURL;
        redisTemplate.delete(key);
        log.info("=================删除幂等性:{}", key);
    }


    /**
     * 抛异常之后，把redis删除
     *
     * @param joinPoint
     */
    @AfterThrowing(POINT_CUT)
    public void afterThrow(JoinPoint joinPoint) {
        ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attribute.getRequest();

        // 请求地址（作为存放cache的key值）
        String requestURL = request.getRequestURI();

        requestURL = requestURL.replaceAll("/",":");

        String key = PREFIX_KEY + requestURL;
        redisTemplate.delete(key);
        log.info("=================删除幂等性:{}", key);
    }


    private Map<String, Object> getParams(CodeSignature codeSignature, ProceedingJoinPoint joinPoint) {
        Map<String, Object> hashMap = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = codeSignature.getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            hashMap.put(paramNames[i], paramValues[i]);
        }
        return hashMap;
    }


}
